//
// Created by 78472 on 2023/3/4.
//

#include "siteManager.h"
#include "siteRequestUtil.h"

using namespace std;
using namespace servicesite;
using namespace httplib;
using namespace std::placeholders;

siteManager* siteManager::Instance = nullptr;

void siteManager::insertEsp32PanelInfo(qlibc::QData esp32PanelEntry)
{
    {
        std::lock_guard<std::mutex> lg(panelListMutex);
        esp32PanelEntry.setBool("online", true);
        string device_id = esp32PanelEntry.getString("device_id");
        auto pos = esp32PanelMap.find(device_id);
        if(pos != esp32PanelMap.end()){
            if(pos->second.asValue() == esp32PanelEntry.asValue()){ // 如果内容相同，则不做任何改变
                auto posCount = esp32PanelCoutMap.find(device_id);
                if(posCount != esp32PanelCoutMap.end()){
                    posCount->second = 3;
                }else{
                    esp32PanelCoutMap.insert(std::make_pair(device_id, 3));
                }
                return;

            }else{
                esp32PanelMap.erase(pos);
                if(esp32PanelCoutMap.find(device_id) != esp32PanelCoutMap.end()){
                    esp32PanelCoutMap.erase(device_id);
                }
            }
        }
        // 插入条目和心跳计数
        esp32PanelMap.insert(std::make_pair(device_id, esp32PanelEntry));
        esp32PanelCoutMap.insert(std::make_pair(device_id, 3));
    }
    // 增加条目，触发在线列表更新
    trigger_updatePanelList();
    trigger_updateSite();
}


void siteManager::updateEsp32PanelThreadFunc()
{
    for(;;){
        std::this_thread::sleep_for(std::chrono::seconds(10));
        bool triggerFlag = false;
        {
            std::lock_guard<std::mutex> lg(panelListMutex);
            for(auto pos = esp32PanelCoutMap.begin(); pos != esp32PanelCoutMap.end(); ){
                pos->second--;
                if(pos->second <= 0){
                    // 删除条目和心跳计数
                    if(esp32PanelMap.find(pos->first) != esp32PanelMap.end()){
                        esp32PanelMap.erase(pos->first);
                    }
                    pos = esp32PanelCoutMap.erase(pos);
                    triggerFlag = true;
                }else{
                    pos++;
                }
            }
        }
        if(triggerFlag){
            // 删除条目，触发在线面板列表更新
            trigger_updatePanelList();
        }
    }
}


void siteManager::updateDevSitesThreadFunc()
{
    //站点更新
    while(true){
        {
            std::unique_lock<std::mutex> ul(updateMutex);
            updateCv.wait(ul, [this]()->bool{ return !updateComplete; });
        }
        updateSite();
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }
}

void siteManager::updateOnlinePanelThreadFunc(){
    //在线面板列表更新
    while(true){
        {
            std::unique_lock<std::mutex> ul(panelListMutex);
            panelListCv.wait(ul, [this]()->bool { return !onlinePanelListUpdate; });
        }
        updatePanelList();
        std::this_thread::sleep_for(std::chrono::seconds(5));
    }
}

void siteManager::allCompleteThreadFunc(){
    while(true){
        {
            std::unique_lock<std::mutex> ul(dgpMutex);
            dgpCv.wait(ul, [this]()->bool { return devGrpUpdateComplete && panListUpdateComplete;});
        }

        // 发布完全更新完毕消息
        qlibc::QData upDateMessageData;
        upDateMessageData.setString("message_id", DeviceGroupPanel_Update_Message);
        upDateMessageData.putData("content", qlibc::QData().setString("phone", getPanelPhone()));
        ServiceSiteManager::getInstance()->publishMessage(DeviceGroupPanel_Update_Message, upDateMessageData.toJsonString());
        LOG_PURPLE << "==>" << DeviceGroupPanel_Update_Message << ": " << upDateMessageData.toJsonString();

        {
            std::lock_guard<std::mutex> ul(dgpMutex);
            devGrpUpdateComplete = false;
            panListUpdateComplete = false;
        }
    }
}

string siteManager::getPanelPhone()
{
    string phone;
    qlibc::QData panelSelfInfoResponse;
    if(siteRequestUtil::access_service_config_get_self_info("127.0.0.1", panelSelfInfoResponse)){
        phone = panelSelfInfoResponse.getData("response").getString("phone");
    }
    return phone;
}

string siteManager::buildDeviceId(string deviceMac)
{
    string device_id = "EDGE_";
    for(int i = 0; i < 6; ++i){
        device_id.append(deviceMac.substr(i*2, 2));
        if(i != 5){
            device_id.append("_");
        }
    }
    return device_id;
}

bool siteManager::get_all_local_sites(string siteIP, std::set<string> &siteIdSet)
{
    qlibc::QData siteResponse;
    if(siteRequestUtil::access_service_query_get_localSite(siteIP, siteResponse)){
        LOG_BLUE << "site_localSite response: " << siteResponse.toJsonString();
        qlibc::QData siteList = siteResponse.getData("response").getData("siteList");
        for(uint i = 0; i < siteList.size(); ++i){
            qlibc::QData siteData = siteList.getArrayElement(i);
            siteIdSet.insert(siteData.getString("site_id"));
        }
        return true;
    }
    return false;
}

bool siteManager::get_devSites(std::set<string> deviceSiteSet, qlibc::QData response, std::map<string, Json::Value>& sitesMap)
{
    bool record{true};
    qlibc::QData node_list = response.getData("response").getData("node_list");
    for(Json::ArrayIndex i = 0; i < node_list.size(); ++i){
        qlibc::QData ithData = node_list.getArrayElement(i);
        string ip = ithData.getString("ip");
        string panelId = ithData.getString("uid");
        //获取每个面板上的设备类站点
        qlibc::QData siteResponse;
        if(siteRequestUtil::access_service_query_get_localSite(ip, siteResponse)){
            LOG_BLUE << "site_localSite response: " << siteResponse.toJsonString();
            qlibc::QData siteList = siteResponse.getData("response").getData("siteList");
            Json::ArrayIndex siteListSize = siteList.size();
            for(Json::ArrayIndex i = 0; i < siteListSize; ++i){
                qlibc::QData siteData = siteList.getArrayElement(i);
                string site_id = siteData.getString("site_id");
                if(deviceSiteSet.find(site_id) != deviceSiteSet.end()){
                    string siteName;
                    siteName.append(panelId).append(":").append(site_id);
                    Json::Value siteItem;
                    siteItem["siteName"] = siteName;
                    siteItem["ip"] = ip;
                    siteItem["port"] = siteData.getInt("port");
                    siteItem["siteId"] = site_id;
                    sitesMap.insert(std::make_pair(siteName, siteItem));
                }
            }
        }else{
            record = false;
            break;
        }
    }

    // 将esp32面板信息加入
    std::lock_guard<std::mutex> lg(panelListMutex);
    for(auto& elem : esp32PanelMap){
        string siteName;
        siteName.append(elem.first).append(":").append(Site_irc_SiteID);
        Json::Value siteItem;
        siteItem["siteName"] = siteName;
        siteItem["ip"] = elem.second.getString("ip");
        siteItem["port"] = ESP32_HTTP_SERVER_PORT;
        siteItem["siteId"] = Site_irc_SiteID;
        
        sitesMap.insert(std::make_pair(siteName, siteItem));
    }

    return record;
}

void siteManager::update_devSites_record(std::map<string, Json::Value> sitesMap)
{
    SiteRecord::getInstance()->removeSitesNonExist(sitesMap);
    for(auto& elem : sitesMap){
        SiteRecord::getInstance()->addSite(elem.first, elem.second["ip"].asString(), elem.second["port"].asInt(), elem.second["siteId"].asString());
    }
    std::set<string> siteNames = SiteRecord::getInstance()->getSiteName();
    LOG_INFO << "********";
    for(auto& siteName : siteNames){
        string ip;
        int port;
        SiteRecord::getInstance()->getSiteInfo(siteName, ip, port);
        LOG_PURPLE << "     ExistSite: " << siteName << ": <" << ip << ", " << port << ">...";
    }
    LOG_INFO << "********";
}

bool siteManager::subscribe_devSites_message(std::set<string> siteNames)
{
    bool subscribe_success_flag = true;
    for(auto& siteName : siteNames){
        smatch sm;
        if(regex_match(siteName, sm, regex("(.*):ble_light"))){     //订阅蓝牙站点的消息
            //获取ip，端口号
            string ip;
            int port;
            if(SiteRecord::getInstance()->getSiteInfo(siteName, ip, port)){
                //订阅蓝牙站点消息
                std::vector<string> messageIdList;
                messageIdList.push_back(ScanResultMsg);
                messageIdList.push_back(SingleDeviceBindSuccessMsg);
                messageIdList.push_back(SingleDeviceUnbindSuccessMsg);
                messageIdList.push_back(BindEndMsg);
                messageIdList.push_back(Device_State_Changed);
                messageIdList.push_back(DeviceListChanged);
                if(ip != "127.0.0.1"){
                    int code = ServiceSiteManager::getInstance()->subscribeMessage(ip, port, "ble_light", messageIdList);
                    if(code != 0){
                        subscribe_success_flag = false;
                    }
                }
            }
            
        }else if(regex_match(siteName, sm, regex("(.*):zigbee_light"))){   //订阅zigbee站点消息
            //获取ip，端口号
            string ip;
            int port;
            if(SiteRecord::getInstance()->getSiteInfo(siteName, ip, port)){
                //订阅蓝牙站点消息
                std::vector<string> messageIdList;
                // messageIdList.push_back(DeviceOnOffLineMsg);
                messageIdList.push_back(SingleDeviceBindSuccessMsg);
                messageIdList.push_back(TriggerSceneMsg);
                // messageIdList.push_back(Device_State_Notify);
                // messageIdList.push_back(Device_Online_Notify);
                messageIdList.push_back(Key_bind_success_messageID);
                if(ip != "127.0.0.1"){
                    int code = ServiceSiteManager::getInstance()->subscribeMessage(ip, port, "zigbee_light", messageIdList);
                    if(code != 0){
                        subscribe_success_flag = false;
                    }
                }
            }

        }else if (regex_match(siteName, sm, regex("(.*):hongmei_dev"))){
            //获取ip，端口号
            string ip;
            int port;
            if(SiteRecord::getInstance()->getSiteInfo(siteName, ip, port)){
                //订阅蓝牙站点消息
                std::vector<string> messageIdList;
                messageIdList.push_back(DeviceOnOffLineMsg);
                messageIdList.push_back(Device_State_Changed);
                if(ip != "127.0.0.1"){
                    int code = ServiceSiteManager::getInstance()->subscribeMessage(ip, port, "hongmei_dev", messageIdList);
                    if(code != 0){
                        subscribe_success_flag = false;
                    }
                }
            }

        }else if(regex_match(siteName, sm, regex("(.*):bt_device"))){
            //获取ip，端口号
            string ip;
            int port;
            if(SiteRecord::getInstance()->getSiteInfo(siteName, ip, port)){
                //订阅蓝牙站点消息
                std::vector<string> messageIdList;
                messageIdList.push_back(DeviceOnOffLineMsg);
                messageIdList.push_back(KeyValue_MessageID);
                if(ip != "127.0.0.1"){
                    int code = ServiceSiteManager::getInstance()->subscribeMessage(ip, port, "bt_device", messageIdList);
                    if(code != 0){
                        subscribe_success_flag = false;
                    }
                }
            }

        }else if(regex_match(siteName, sm, regex("(.*):site_irc"))){
            //获取ip，端口号
            string ip;
            int port;
            if(SiteRecord::getInstance()->getSiteInfo(siteName, ip, port)){
                //订阅红外站点消息
                std::vector<string> messageIdList;
                messageIdList.push_back(SingleDeviceBindSuccessMsg);
                messageIdList.push_back(SingleDeviceUnbindSuccessMsg);
                messageIdList.push_back(DeviceListChanged);
                messageIdList.push_back(Key_bind_success_messageID);
                if(ip != "127.0.0.1"){
                    int code = ServiceSiteManager::getInstance()->subscribeMessage(ip, port, "site_irc", messageIdList);
                    if(code != 0){
                        subscribe_success_flag = false;
                    }
                }
            }
        }
    }

    return subscribe_success_flag;
}

/**
 * 对在线列表执行如下操作：
 *      剔除已经不属于本账号的面板
 *      增加属于本账号但是离线的面板
 */
 void siteManager::amendPanelList_with_whiteList(qlibc::QData& panelList){
    string phone = getPanelPhone();
    std::map<string, qlibc::QData> panelMap;
    for(int i = 0; i < panelList.size(); ++i){
        qlibc::QData item = panelList.getArrayElement(i);
        panelMap.insert(std::make_pair(item.getString("device_mac"), item));
    }

    qlibc::QData whiteListResponse;
    if(siteRequestUtil::access_service_config_whiteListRequest("127.0.0.1", whiteListResponse)){
        qlibc::QData devices = whiteListResponse.getData("response").getData("info").getData("devices");
        for(int i = 0; i < devices.size(); ++i){
            qlibc::QData item = devices.getArrayElement(i);
            string category_code = item.getString("category_code");
            string device_mac = item.getString("device_mac");
            string itemPhone = item.getString("phone");
            string state = item.getString("state");

            if(state == "error"){
                //从在线面板列表里删除已经不属于该账号的面板
                auto pos = panelMap.find(device_mac);
                if(pos != panelMap.end()){
                    panelMap.erase(pos);
                }

            }else{
                //属于该账号，但是不在在线面板列表里
                if(itemPhone != phone || category_code != "audiopanel")  continue;
                if(panelMap.find(device_mac) == panelMap.end()){  //属于该账号，但不在在线面板列表里
                    Json::Value offLinePanelItem;
                    offLinePanelItem["device_mac"] = device_mac;
                    offLinePanelItem["device_id"] = buildDeviceId(device_mac);
                    offLinePanelItem["device_name"] = item.getString("device_name");
                    offLinePanelItem["site_id"] = Json::Value::null;
                    offLinePanelItem["online"] = false;
                    offLinePanelItem["location"]["room_name"] = item.getString("room_name");
                    offLinePanelItem["location"]["room_no"] = item.getString("room_no");

                    panelMap.insert(std::make_pair(device_mac, offLinePanelItem));
                }
            }
        }
    }

    panelList.clear();
    for(auto& elem : panelMap){
        panelList.append(elem.second);
    }
 }

/**
 * @brief 
 *      删除不在面板列表里的设备和组
 */
 void siteManager::amendDevGrpList_with_panelList(qlibc::QData &device_list, qlibc::QData &group_list, qlibc::QData &panel_list)
 {
    std::map<string, Json::Value> device_list_map;
    for(int i = 0; i < device_list.size(); ++i){
        qlibc::QData item = device_list.getArrayElement(i);
        device_list_map.insert(std::make_pair(item.getString("device_uid"), item.asValue()));
    }

    std::map<string, Json::Value> group_list_map;
    for(int i = 0; i < group_list.size(); ++i){
        qlibc::QData item = group_list.getArrayElement(i);
        group_list_map.insert(std::make_pair(item.getString("group_uid"), item.asValue()));
    }

    std::set<string> panel_list_map;
    for(int i = 0; i < panel_list.size(); ++i){
        qlibc::QData item = panel_list.getArrayElement(i);
        panel_list_map.insert(item.getString("device_id"));
    }

    //删除设备列表、组列表中不属于当前面板的设备
    for(auto pos = device_list_map.begin(); pos != device_list_map.end();){
        string panelId;
        smatch sm;
        if(regex_match(pos->first, sm, regex("(.*)>(.*):(.*)"))){
            panelId = sm.str(2);
        }
        if(!panelId.empty() && panel_list_map.find(panelId) == panel_list_map.end()){
            pos = device_list_map.erase(pos);
        }else{
            pos++;
        }
    }

    for(auto pos = group_list_map.begin(); pos != group_list_map.end();){
        string panelId;
        smatch sm;
        if(regex_match(pos->first, sm, regex("(.*)>(.*):(.*)"))){
            panelId = sm.str(2);
        }
        if(!panelId.empty() && panel_list_map.find(panelId) == panel_list_map.end()){
            pos = group_list_map.erase(pos);
        }else{
            pos++;
        }
    }

    device_list.clear();
    group_list.clear();
    for(auto elem : device_list_map){
        device_list.append(elem.second);
    }
    for(auto elem : group_list_map){
        group_list.append(elem.second);
    }
 }


//触发站点更新
void siteManager::trigger_updateSite(){
    LOG_BLUE << "trigger_updateSite: update devsites devicelist grouolist ...";
    {
        std::lock_guard<std::mutex> lg(updateMutex);
        updateComplete = false;
    }
    updateCv.notify_one();
}

//触发面板列表更新
void siteManager::trigger_updatePanelList(){
    LOG_BLUE << "trigger_updatePanelList: update online panel list ...";
    {
        std:lock_guard<std::mutex> lg(panelListMutex);
        onlinePanelListUpdate = false;
    }
    panelListCv.notify_one();
}

/*
    1. 通过主从站点获取局域网内的所有面板列表
    2. 访问每个面板上的查询站点，获取该面板上的设备类站点。组装为所有面板的设备类站点。
    3. SiteRecord中删除不存在的站点记录，加入新发现的站点记录
    4. 打印SiteRecord中所有的设备类站点条目
    5. 订阅设备类站点的相关消息
    6. 更新设备列表、组列表
*/
bool siteManager::updateSite(){
    bool record_thisTime{true};
    //待管理的设备类站点列表
    std::set<string> deviceSiteSet{BleSiteID, TvAdapterSiteID, ZigbeeSiteID, BtDeviceSiteID, Site_irc_SiteID};

    //从主从站点获取所有面板列表
    qlibc::QData response;
    if(!siteRequestUtil::access_service_master_get_all("127.0.0.1", response)){
        std::lock_guard<std::mutex> lg(updateMutex);
        updateComplete = false;
        updateCv.notify_one();
        return false;
    }
    LOG_BLUE << "get_all response: " << response.toJsonString();

    //获取设备类站点
    std::map<string, Json::Value> sitesMap;
    if(!get_devSites(deviceSiteSet, response, sitesMap)){
        std::lock_guard<std::mutex> lg(updateMutex);
        updateComplete = false;
        updateCv.notify_one();
        return false;
    }

    //更新设备类站点记录
    update_devSites_record(sitesMap);
    //订阅设备类站点消息
    bool subscribe_success_flag = subscribe_devSites_message(SiteRecord::getInstance()->getSiteName());
    //更新设备列表
    bool updateDeviceList = DeviceManager::getInstance()->updateDeviceList();
    //更新组列表
    bool updateGroupList = GroupManager::getInstance()->updateGroupList();

    //发布列表变更消息
    qlibc::QData deviceGroupUpdate;
    deviceGroupUpdate.setString("message_id", DeviceGroupList_Update_Message);
    deviceGroupUpdate.putData("content", qlibc::QData().setString("phone", getPanelPhone()));
    ServiceSiteManager::getInstance()->publishMessage(DeviceGroupList_Update_Message, deviceGroupUpdate.toJsonString());
    LOG_PURPLE << "==>" << DeviceGroupList_Update_Message << ": " << deviceGroupUpdate.toJsonString();

    //更新灯带列表
    record_thisTime = subscribe_success_flag && updateDeviceList && updateGroupList;

    {
        std::lock_guard<std::mutex> lg(updateMutex);
        updateComplete = record_thisTime;
    }
    updateCv.notify_one();

    {
        std::lock_guard<std::mutex> lg(dgpMutex);
        devGrpUpdateComplete = record_thisTime;
    }
    dgpCv.notify_one();

    return record_thisTime;
}


bool siteManager::updatePanelList(){
    bool record_thisTime{true};
    std::map<string, qlibc::QData> panelMap;

    //从主从获取所有面板列表
    qlibc::QData response;
    if(siteRequestUtil::access_service_master_get_all("127.0.0.1", response)){
        LOG_BLUE << "get_all response: " << response.toJsonString();
        qlibc::QData node_list = response.getData("response").getData("node_list");
        for(Json::ArrayIndex i = 0; i < node_list.size(); ++i){
            //获取该面板下的所有站点
            qlibc::QData ithData = node_list.getArrayElement(i);
            string ip = ithData.getString("ip");
            string mac = ithData.getString("mac");
           
            std::set<string> siteIdSet;
            if(!get_all_local_sites(ip, siteIdSet)){
                record_thisTime = false;
                LOG_RED << "get local sites from " << ip << " failed ...";
                break;
            }

            //如果该面板包含zigbee_light或者ble_light则形成一个面板条目
            qlibc::QData data;
            if(siteIdSet.find("ble_light") != siteIdSet.end()){
                data.setString("siteId", "ble_light");
            }else if(siteIdSet.find("zigbee_light") != siteIdSet.end()){
                data.setString("siteId", "zigbee_light");
            }
            
            data.setString("device_id", ithData.getString("uid"));
            data.setString("device_mac", ithData.getString("mac"));
            data.setString("device_name", ithData.getString("name"));
            data.setString("panelType", ithData.getString("panelType"));
            data.setBool("online", true);
            data.setString("ip", ip);
            data.putData("location", ithData.getData("location"));
            
            panelMap.insert(std::make_pair(mac, data));
        }

    }else{  //获取局域网内所有发现的站点
        record_thisTime = false;
        LOG_RED << "access master service get_all failed ...";
    }

    {
        std::lock_guard<std::mutex> lg(panelListMutex);
        onlinePanelListUpdate = record_thisTime;
        if(record_thisTime){
            LOG_INFO << "update online panel list success ...";
            panelListData.clear();
            // 3308面板
            for(auto elem : panelMap){
                panelListData.append(qlibc::QData(elem.second));
            }
            // esp32面板
            for(auto elem : esp32PanelMap){
                panelListData.append(qlibc::QData(elem.second));
            }
        }
    }
    panelListCv.notify_one();

    {
        std::lock_guard<std::mutex> lg(dgpMutex);
        panListUpdateComplete = record_thisTime;
    }
    dgpCv.notify_one();

    return record_thisTime;
}

qlibc::QData siteManager::getPanelList(){
    std::lock_guard<std::mutex> lg(panelListMutex);
    return panelListData;
}


qlibc::QData siteManager::getAllDeviceInfo(){
    qlibc::QData device_list = DeviceManager::getInstance()->getAllDeviceList();
    qlibc::QData group_list = GroupManager::getInstance()->getAllGroupList();
    qlibc::QData panel_list = getPanelList();

    amendPanelList_with_whiteList(panel_list);
    amendDevGrpList_with_panelList(device_list, group_list, panel_list);

    qlibc::QData info;
    info.putData("device_list", device_list);
    info.putData("group_list", group_list);
    info.putData("panelList", panel_list);
    return info;
}


